/**
 * Copyright 2010 Adam Ruggles.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package saig.engine.models;

import java.awt.Point;

import saig.engine.Context;
import saig.engine.Engine;
import saig.engine.models.enums.ModelType;
import saig.engine.models.interfaces.Damageable;
import saig.engine.resources.ImageLoader;


/**
 * The player model.
 * @author Adam
 * @version $Id$
 * 
 * Created on Jul 10, 2010 at 9:12:46 PM 
 */
public final class Player extends Model implements Damageable {
	/**
	 * Player Modes.
	 */
	public enum Mode {
		FIRE, LARGE, RACOON, SMALL
	}
	/**
	 * Player States.
	 */
	public enum State {
		DEAD, DUCK, JUMP, RUN, STAND, WALK
	}
	/**
	 * If the player can jump or not.
	 */
	private boolean canJump;
	/**
	 * The number of coins collected.
	 */
	private int coinsCollected;
	/**
	 * The number of flowers collected.
	 */
	private int flowersCollected;
	/**
	 * Used as a frame speed value.
	 */
	private int frameSpeed;
	/**
	 * The amount of ticks the frame has been shown.
	 */
	private int frameTime;
	/**
	 * InvulnerableCount used to keep track of the amount of ticks the player is invulnerable.
	 */
	private int invulnerableCount = 0;
	/**
	 * The amount of time a user can hold down the jump button.
	 */
	private int jumpTime;
	/**
	 * Mario's current mode.
	 */
	private Mode mode;
	/**
	 * The number of mushrooms collected.
	 */
	private int mushroomsCollected;
	/**
	 * Mario's current state.
	 */
	private State state;
	/**
	 * Constructs the player model object.
	 */
	public Player(final Context context) {
		super(context, ModelType.PLAYER);
		mode = Mode.LARGE;
		state = State.STAND;
		jumpTime = 0;
		canJump = true;
		coinsCollected = 0;
		frameSpeed = 2;
		// TODO change these values for getSprites.
		frames = ImageLoader.getInstance().getSprites(ImageLoader.PLAYER_FRAMES, 0 , 0);
	}
	/**
	 * Adds 1 to the number of coins that have been collected.
	 */
	public void addCoinCollected() {
		coinsCollected++;
	}
	/**
	 * Adds 1 to the number of flowers that have been collected.
	 */
	public void addFlowersCollected() {
		flowersCollected++;
	}
	/**
	 * Adds 1 to the number of mushrooms that have been collected.
	 */
	public void addMushroomsCollected() {
		mushroomsCollected++;
	}
	/**
	 * {@inheritDoc}
	 * @see saig.engine.models.Model#collisionOccurredTop(java.awt.Point)
	 */
	@Override
	protected void collisionOccurredTop(final Point p) {
		super.collisionOccurredTop(p);
		jumpTime = 0;
	}
	/**
	 * Returns the number of coins that have been collected.
	 * @return the coinsCollected.
	 */
	public int getCoinsCollected() {
		return coinsCollected;
	}
	/**
	 * Returns the number of flowers that have been collected.
	 * @return the flowersCollected.
	 */
	public int getFlowersCollected() {
		return flowersCollected;
	}
	/**
	 * {@inheritDoc}
	 * @see saig.engine.models.Model#getHeight()
	 */
	@Override
	public int getHeight() {
		if (mode == Mode.SMALL) {
			return 16;
		}
		return 27;
	}
	/**
	 * Returns the number of mushrooms that have been collected.
	 * @return the mushroomsCollected.
	 */
	public int getMushroomsCollected() {
		return mushroomsCollected;
	}
	/**
	 * {@inheritDoc}
	 * @see saig.engine.models.Model#getWidth()
	 */
	@Override
	public int getWidth() {
		return 8;
	}
	/**
	 * Returns canJump.
	 * @return the canJump.
	 */
	public boolean isCanJump() {
		return canJump;
	}
	/**
	 * {@inheritDoc}
	 * @see saig.engine.models.interfaces.Damageable#isDead()
	 */
	@Override
	public boolean isDead() {
		return (state == State.DEAD);
	}
	/**
	 * {@inheritDoc}
	 * @see saig.engine.models.Model#performUpdate()
	 */
	@Override
	public void performUpdate() {
		//float accelY = 0;
		float accelX = 0;
		boolean[] keyState = context.getKeyState();
		onGround = checkOnGround();
		// Handle jumping.
		/*
		if (keyState[Engine.KEY_A] && onGround && mayJump && jumpTime == 0) {
			// Jump
			accelY = -1.90f;
			jumpTime = 10;
		} else if (keyState[Engine.KEY_A] && jumpTime > 0) {
			accelY = -0.5f - GRAVITY;
			jumpTime--;
		} else if (jumpTime > 0) {
			jumpTime--;
		}
		*/
		// Jump v2
		if (keyState[Engine.KEY_A]) {
			if (onGround && canJump) {
				// accelY = -1.9f;
				jumpTime = 10;
				dy = jumpTime * -1.5f;
				onGround = false;
			} else if (jumpTime > 0) {
				dy = jumpTime * -1.5f;
				jumpTime--;
			}
		} else {
			jumpTime = 0;
		}
		// Left Right movement.
		if (keyState[Engine.KEY_RIGHT]) {
			accelX = 0.6f;
			direction = 1;
		} else if (keyState[Engine.KEY_LEFT]) {
			accelX = 0.6f;
			direction = -1;
		}
		if (keyState[Engine.KEY_B]) {
			// TODO fire weapon.
			// Speed/Fire Key
			accelX *= 2;
			frameSpeed = 1;
		} else {
			frameSpeed = 2;
		}
		// Set acceleration deltas.
		dx = ((dx * FRICTION) + (direction * accelX));
		dy *= AIR_RESISTANCE;
		// If the change in x acceleration is less than a certain amount set it to zero.
		if (Math.abs(dx) < .5f) {
			dx = 0;
		}
		if (onGround) {
			// Change state.
			if (dx == 0) {
				state = State.STAND;
			} else {
				state = State.WALK;
			}
			canJump = !keyState[Engine.KEY_A];
		} else {
			// Change state
			state = State.JUMP;
			// Apply gravity.
			dy += GRAVITY;
			// Cap the y acceleration, this is for the collision detection.
			if (Math.abs(dy) >= context.getLevel().getTileHeight()) {
				dy = context.getLevel().getTileHeight() - 1;
			}
			canJump = false;
		}
		setFrame();
		checkTileCollision();
		move();
		if (invulnerableCount > 0) {
			invulnerableCount--;
		}
	}
	/**
	 * The player stomped on another model.
	 */
	public void stomped() {
		jumpTime = 10;
		dy = jumpTime * -1.5f;
		onGround = false;
	}
	/**
	 * Sets the correct animation frame for the player.
	 */
	public void setFrame() {
		frameTime++;
		switch (mode) {
		case LARGE:
			if (state == State.STAND) {
				frame = 0;
				frameTime = 0;
			} else if (state == State.WALK) {
				if (frame == 0 && frameTime > frameSpeed) {
					frame = 1;
					frameTime = 0;
				} else if (frame == 1 && frameTime > frameSpeed){
					frame = 2;
					frameTime = 0;
				} else if (frameTime > frameSpeed){
					frame = 0;
					frameTime = 0;
				}
			} else if (state == State.JUMP) {
				frame = 6;
				frameTime = frameSpeed;
			}
			break;
		case FIRE:
			if (state == State.STAND) {
				frame = 15;
				frameTime = 0;
			}
			break;
		case RACOON:
			if (state == State.STAND) {
				frame = 30;
				frameTime = 0;
			}
			break;
		case SMALL:
		default:
			if (state == State.STAND) {
				frame = 45;
				frameTime = 0;
			} else if (state == State.WALK) {
				if (frame == 45 && frameTime > frameSpeed) {
					frame = 46;
					frameTime = 0;
				} else if (frame == 46 && frameTime > frameSpeed){
					frame = 45;
					frameTime = 0;
				}
			}
		}
		if (frameTime > frameSpeed) {
			frameTime = frameSpeed;
		}
	}
	/**
	 * Sets jumpTime.
	 * @param jumpTime the jumpTime to set.
	 */
	public void setJumpTime(int jumpTime) {
		this.jumpTime = jumpTime;
	}
	/**
	 * {@inheritDoc}
	 * @see saig.engine.models.interfaces.Damageable#takeDamage()
	 */
	@Override
	public void takeDamage() {
		if (invulnerableCount > 0) {
			return;
		}
		switch (mode) {
		case SMALL:
			state = State.DEAD;
			break;
		case LARGE:
			mode = Mode.SMALL;
			invulnerableCount = 20;
			break;
		case FIRE:
			// Same as RACOON.
		case RACOON:
			mode = Mode.LARGE;
			invulnerableCount = 20;
		}
	}
}
